/////////////////////////////////////////////////////////////////////////
//
// Amuse Engine SDK - core/math
// Copyright (c) 2014.  All Rights Reserved
//
// File:		AEMath.inl
// Author:		Gianluca Belardelli
// Date:		29/07/2013
//
/////////////////////////////////////////////////////////////////////////
#ifndef _AEMATH_INL_
#define _AEMATH_INL_

#include <math.h>

#if !defined(AEMATH_SQRT_FUNCS)
AE_FORCEINLINE AEFLOAT32 AE_CALL AEMath::Sqrt( const AEFLOAT32 fValue )
{
	return sqrtf( fValue );
}

AE_FORCEINLINE AEDOUBLE64 AE_CALL AEMath::Sqrt( const AEDOUBLE64 dValue )
{
	return sqrt( dValue );
}

AE_FORCEINLINE AEFLOAT32 AE_CALL AEMath::SqrtInverse( const AEFLOAT32 fValue )
{
	return ( 1.0f / sqrtf( fValue ) );
}

AE_FORCEINLINE AEDOUBLE64 AE_CALL AEMath::SqrtInverse( const AEDOUBLE64 dValue )
{
	return ( 1.0 / sqrt( dValue ) );
}
#endif // AEMATH_SQRT_FUNCS

#if !defined(AEMATH_ABS_FUNCS)
AE_FORCEINLINE AEFLOAT32 AE_CALL AEMath::Abs( const AEFLOAT32 fValue )
{
	return ( fValue < 0 ? -fValue : fValue );
}

AE_FORCEINLINE AEDOUBLE64 AE_CALL AEMath::Abs( const AEDOUBLE64 dValue )
{
	return ( dValue < 0 ? -dValue : dValue );
}

AE_FORCEINLINE AEINT32 AE_CALL AEMath::Abs( const AEINT32 iValue )
{
	return ( iValue < 0 ? -iValue : iValue );
}
#endif // AEMATH_ABS_FUNCS

AE_FORCEINLINE AEFLOAT32 AEMath::Exp( AEFLOAT32 fExponent )
{
	return expf( fExponent );
}

AE_FORCEINLINE AEFLOAT32 AEMath::Pow( AEFLOAT32 fBase, AEFLOAT32 fExponent )
{
	return powf( fBase, fExponent );
}

template<class T>
AE_FORCEINLINE T AEMath::Square( T value )
{
	 return ( value * value );
}

AE_FORCEINLINE AEFLOAT32 AEMath::Pow2( AEFLOAT32 fExponent )
{
	return pow( 2.0f, fExponent );
}

AE_FORCEINLINE AEUINT32 AEMath::Pow2( AEUINT32 uiExponent )
{
	return ( 1U << uiExponent );
}

#if !defined( AEMATH_CEIL_FUNCS )
AE_FORCEINLINE AEFLOAT32 AE_CALL AEMath::Ceil( const AEFLOAT32 fValue )
{
	return ceilf( fValue );
}

AE_FORCEINLINE AEDOUBLE64 AE_CALL AEMath::Ceil( const AEDOUBLE64 dValue )
{
	return ceil( dValue );
}

#endif // AEMATH_CEIL_FUNCS

#if !defined( AEMATH_FLOOR_FUNCS )
AE_FORCEINLINE AEFLOAT32 AE_CALL AEMath::Floor( const AEFLOAT32 fValue )
{
	return floorf( fValue );
}

AE_FORCEINLINE AEDOUBLE64 AE_CALL AEMath::Floor( const AEDOUBLE64 dValue )
{
	return floor( dValue );
}
#endif // AEMATH_FLOOR_FUNCS

#if !defined( AEMATH_SIN_FUNCS )
AE_FORCEINLINE AEFLOAT32 AE_CALL AEMath::Sin( const AEFLOAT32 fRadValue )
{
	return sinf( fRadValue );
}

AE_FORCEINLINE AEDOUBLE64 AE_CALL AEMath::Sin( const AEDOUBLE64 dRadValue )
{
	return sin( dRadValue );
}
#endif // AEMATH_SIN_FUNCS

#if !defined( AEMATH_COS_FUNCS )
AE_FORCEINLINE AEFLOAT32 AE_CALL AEMath::Cos( const AEFLOAT32 fRadValue )
{
	return cosf( fRadValue );
}

AE_FORCEINLINE AEDOUBLE64 AE_CALL AEMath::Cos( const AEDOUBLE64 dRadValue )
{
	return cos( dRadValue );
}
#endif // AEMATH_COS_FUNCS

#if !defined( AEMATH_TAN_FUNCS )
AE_FORCEINLINE AEFLOAT32 AE_CALL AEMath::Tan( const AEFLOAT32 fRadValue )
{
	return tanf( fRadValue );
}

AE_FORCEINLINE AEDOUBLE64 AE_CALL AEMath::Tan( const AEDOUBLE64 dRadValue )
{
	return tan( dRadValue );
}
#endif // AEMATH_TAN_FUNCS

#if !defined( AEMATH_ASIN_FUNCS )
AE_FORCEINLINE AEFLOAT32 AE_CALL AEMath::Asin( const AEFLOAT32 fRadValue )
{
	return asinf( fRadValue );
}

AE_FORCEINLINE AEDOUBLE64 AE_CALL AEMath::Asin( const AEDOUBLE64 dRadValue )
{
	return asinf( dRadValue );
}
#endif // AEMATH_ASIN_FUNCS

#if !defined( AEMATH_ACOS_FUNCS )
AE_FORCEINLINE AEFLOAT32 AE_CALL AEMath::Acos( const AEFLOAT32 fRadValue )
{
	return acosf( fRadValue );
}

AE_FORCEINLINE AEDOUBLE64 AE_CALL AEMath::Acos( const AEDOUBLE64 dRadValue )
{
	return acosf( dRadValue );
}
#endif // AEMATH_ACOS_FUNCS

#if !defined( AEMATH_ATAN_FUNCS )
AE_FORCEINLINE AEFLOAT32 AE_CALL AEMath::Atan( const AEFLOAT32 fRadValue )
{
	return atanf( fRadValue );
}

AE_FORCEINLINE AEDOUBLE64 AE_CALL AEMath::Atan( const AEDOUBLE64 dRadValue )
{
	return atan( dRadValue );
}
#endif // AEMATH_ATAN_FUNCS

#if !defined( AEMATH_ATAN2_FUNCS )
AE_FORCEINLINE AEFLOAT32 AE_CALL AEMath::Atan2( const AEFLOAT32 fRadY, const AEFLOAT32 fRadX )
{
	return atan2f( fRadY, fRadX );
}

AE_FORCEINLINE AEDOUBLE64 AE_CALL AEMath::Atan2( const AEDOUBLE64 dRadY, const AEDOUBLE64 dRadX )
{
	return atan2f( dRadY, dRadX );
}
#endif // AEMATH_ATAN2_FUNCS

AE_FORCEINLINE AEBOOL32 AEMath::IsAngleEqualDeg( AEFLOAT32 fAngle1, AEFLOAT32 fAngle2, AEFLOAT32 fEpsilon )
{
	AEASSERT( fEpsilon >= 0.0f );
	
	while( fAngle1 < 0.0f )
		fAngle1 += 360.0f;
		
	while( fAngle2 < 0.0f )
		fAngle2 += 360.0f;
	
	while( fAngle1 >= 360.0f )
		fAngle1 -= 360.0f;
		
	while( fAngle2 >= 360.0f )
		fAngle2 -= 360.0f;
	
	return IsFloatEqual( fAngle1, fAngle2, fEpsilon );
}

AE_FORCEINLINE AEFLOAT32 AEMath::Log( AEFLOAT32 fValue )
{
	return logf( fValue );
}

AE_FORCEINLINE AEFLOAT32 AEMath::Log2( AEFLOAT32 fValue )
{
	return log10( fValue ) / log10( 2.0f );
}

AE_FORCEINLINE AEFLOAT32 AEMath::Log10( AEFLOAT32 fValue )
{
	return log10f( fValue );
}

AE_FORCEINLINE AEFLOAT32 AEMath::LogB( AEFLOAT32 fValue, AEFLOAT32 fBase )
{
	return log10( fValue ) / log10( fBase );
}

#ifndef AEMATH_FLTEQFUNC

AE_FORCEINLINE AEBOOL32 AEMath::IsFloatEqual( AEFLOAT32 f1, AEFLOAT32 f2, AEFLOAT32 fEpsilon )
{
	// epsilon MUST be larger than zero, otherwise hkMath::equal will fail
	AEASSERT( fEpsilon >= 0.0f );
	
	if( fEpsilon == 0.0f )
		return ( f1 == f2 );
	
	// phrasing the test this way will make sure that NaN's will also return 'false'
	return ( ( f1 >= f2 - fEpsilon ) && ( f1 <= f2 + fEpsilon ) );
}

AE_FORCEINLINE AEBOOL32 AEMath::IsFloatEqual( double d1, double d2, double dEpsilon )
{
	// epsilon MUST be larger than zero, otherwise hkMath::equal will fail
	AEASSERT( dEpsilon >= 0.0 );
	
	if( dEpsilon == 0.0 )
		return ( d1 == d2 );
	
	// phrasing the test this way will make sure that NaN's will also return 'false'
	return ( ( d1 >= d2 - dEpsilon ) && ( d1 <= d2 + dEpsilon ) );
}
#endif // AEMATH_FLTEQFUNC

AE_FORCEINLINE AEBOOL32 AEMath::IsZero( AEFLOAT32 fValue, AEFLOAT32 fEpsilon )
{
	return IsFloatEqual( fValue, 0.0f, fEpsilon );
}

AE_FORCEINLINE AEBOOL32 AEMath::IsZero( AEDOUBLE64 dValue, AEDOUBLE64 dEpsilon )
{
	return IsFloatEqual( dValue, 0.0, dEpsilon );
}

template<class T>
AE_FORCEINLINE T AEMath::Min( T value1, T value2 )
{
	return ( value1 < value2 ? value1 : value2 );
}

template<class T>
AE_FORCEINLINE T AEMath::Min( T value1, T value2, T value3 )
{
	return AEMath::Min( AEMath::Min( value1, value2 ), value3 );
}

template<class T>
AE_FORCEINLINE T AEMath::Min( T value1, T value2, T value3, T value4 )
{
	return AEMath::Min( AEMath::Min( value1, value2 ), AEMath::Min( value3, value4 ) );
}

template<class T>
AE_FORCEINLINE T AEMath::Min( T value1, T value2, T value3, T value4, T value5 )
{
	return AEMath::Min( AEMath::Min( value1, value2, value3, value4 ), value5 );
}

template<class T>
AE_FORCEINLINE T AEMath::Max( T value1, T value2 )
{
	return ( value1 < value2 ? value2 : value1 );
}

template<class T>
AE_FORCEINLINE T AEMath::Max( T value1, T value2, T value3 )
{
	return AEMath::Max( AEMath::Max( value1, value2 ), value3 );
}

template<class T>
AE_FORCEINLINE T AEMath::Max( T value1, T value2, T value3, T value4 )
{
	return AEMath::Max( AEMath::Max( value1, value2 ), AEMath::Max( value3, value4 ) );
}

template<class T>
AE_FORCEINLINE T AEMath::Max (T value1, T value2, T value3, T value4, T value5)
{
	return AEMath::Max( AEMath::Max( value1, value2, value3, value4 ), value5 );
}
/*
template <typename T1, typename T2, typename T3>
AE_FORCEINLINE static T1 AE_CALL AEMath::Clamp( T1 value, T2 min, T3 max )
{
	if ( value < (T1)min ) return (T1)min;
	if ( value > (T1)max ) return (T1)max;
	return value;
}
*/
template<class T>
AE_FORCEINLINE T AEMath::Saturate( T value )
{
	return Clamp( value, T( 0 ), T( 1 ) );
}

#if !defined( AEMATH_MOD_FUNCS )
AE_FORCEINLINE AEFLOAT32 AE_CALL AEMath::Mod( const AEFLOAT32 fValue, const AEFLOAT32 fMod )
{
	return fValue - ( fMod * Float2Int( fValue / fMod ) );
}

AE_FORCEINLINE AEDOUBLE64 AE_CALL AEMath::Mod( const AEDOUBLE64 dValue, const AEDOUBLE64 dMod )
{
	return AEDOUBLE64(Mod(dValue, dMod));
}
#endif // AEMATH_MOD_FUNCS

AE_FORCEINLINE bool AEMath::IsPowerOf( AEUINT32 uiX, AEUINT32 uiBase )
{
	AEASSERT( uiBase > 1 );
	
	if( uiX == 1 )
		return true;
	
	while( uiX > uiBase )
	{
		if( uiX % uiBase == 0 )
			uiX /= uiBase;
		else
			return false;
	}
	
	return ( uiX == uiBase );
}

AE_FORCEINLINE bool AEMath::IsPowerOf2( AEUINT32 uiX )
{
	return ( ( uiX != 0 ) && ( ( uiX & ( uiX - 1U ) ) == 0 ) );
}

template<class T>
AE_FORCEINLINE T AEMath::Interpolate( const T &val1, const T &val2, float fInterpolation )
{
	return val1 * ( 1.0f - fInterpolation ) + val2 * fInterpolation;
}

template<class T>
AE_FORCEINLINE T AEMath::Interpolate( const T &val1, const T &val2, double dInterpolation )
{
	return val1 * ( 1.0 - dInterpolation ) + val2 * dInterpolation;
}


AE_FORCEINLINE AEFLOAT32 AEMath::Root( AEFLOAT32 fValue, AEFLOAT32 fNthRoot )
{
	AEASSERT( ( fValue >= 0.0f ) && ( fNthRoot != 0.0f ) );
	
	return pow( fValue, 1.0f / fNthRoot );
}

template<class T>
AE_FORCEINLINE T AEMath::Sign( T value )
{
	if( value == 0 )
		return 0;
	
	return value < 0 ? (T) (-1) : (T) (1);
}

AE_FORCEINLINE AEFLOAT32 AEMath::Round( AEFLOAT32 fValue )
{
	return floor( fValue + 0.5f );
}

AE_FORCEINLINE AEFLOAT32 AEMath::RoundTo( AEFLOAT32 fValue, AEFLOAT32 fRoundTo )
{
	return ( Round( fValue / fRoundTo ) * fRoundTo );
}

#if !defined( AEMATH_FLOATTOINT_FUNCS )
AE_FORCEINLINE AEINT32 AE_CALL AEMath::Float2Int( const AEFLOAT32 fValue )
{ 
	return ( AEINT32 )fValue; 
}

AE_FORCEINLINE AEINT32 AE_CALL AEMath::Float2Int( const AEDOUBLE64 dValue )
{ 
	return ( AEINT32 )dValue; 
}
#endif // AEMATH_FLOATTOINT_FUNCS

AE_FORCEINLINE AEFLOAT32 AEMath::Fraction( AEFLOAT32 fValue )
{
	return fValue - Float2Int( fValue );
}

template<class T>
AE_FORCEINLINE void AEMath::Swap( T &value1, T &value2 )
{
	T temp = value1;
	value1 = value2;
	value2 = temp;
}

AE_FORCEINLINE AEUINT32 AEMath::PowerOf2Floor( AEUINT32 uiX )
{
	return PowerOf2Ceil( uiX / 2 + 1 );
}

AE_FORCEINLINE AEUINT32 AEMath::PowerOf2Ceil( AEUINT32 uiX )
{		
	// http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
	
	// The method below returns 0 for x == 0, so fix this case manually
	uiX += ( uiX == 0 );
	
	uiX--;
	uiX |= uiX >> 1;
	uiX |= uiX >> 2;
	uiX |= uiX >> 4;
	uiX |= uiX >> 8;
	uiX |= uiX >> 16;
	uiX++;
	
	return uiX;
}

AE_FORCEINLINE float AEMath::GenerateNaN( void )
{
	union 
	{
		float fVal;
		AEUINT32 uiVal;
	}uVal;
	
	// Usually NAN == (exponent = all 1, mantissa = non-zero)
	//         INF == (exponent = all 1, mantissa = zero)
	
	// = 0111 1111 1000 0000 0000 0000 0000 0001
	uVal.uiVal = 0x7f800001;
	
	return uVal.fVal;
}

AE_FORCEINLINE bool AEMath::IsNaN( float fValue )
{
	// Check the 8 exponent bits.
	// Usually NAN == (exponent = all 1, mantissa = non-zero)
	//         INF == (exponent = all 1, mantissa = zero)
	// This simply checks the exponent
	
	union 
	{
		float fVal;
		AEUINT32 uiVal;
	}uVal;
	
	uVal.fVal = fValue;
	return ( ( ( uVal.uiVal & 0x7f800000 ) == 0x7f800000 ) && ( ( uVal.uiVal & 0x7FFFFF ) != 0 ) );

	// Note: This code does not run in 64 Bit (Release), probably because the compiler optimizes too much away.
	//return (! (f == f));
}

#ifndef AEMATH_FINITEFUNCS
AE_FORCEINLINE AEBOOL32 AE_CALL AEMath::IsFiniteNumber( float fValue )
{
	// Check the 8 exponent bits.
	// Usually NAN == (exponent = all 1, mantissa = non-zero)
	//         INF == (exponent = all 1, mantissa = zero)
	// This simply checks the exponent
	
	union 
	{
		float fVal;
		AEUINT32 uiVal;
	}uVal;
	
	uVal.fVal = fValue;
	return ( ( uVal.uiVal & 0x7f800000 ) != 0x7f800000 );

	// If f is inifinity or -infinity one of these comparisions will fail
	// If f is NaN, both comparisions will fail
	
	// Thus this function will only return true for a valid, finite number
}
#endif // AEMATH_FINITEFUNCS

template<class T>
AE_FORCEINLINE bool AEMath::IsInRange( T value, T min, T max )
{
	if( min < max )
		return ( value >= min ) && ( value <= max );
	else
		return ( value <= min ) && ( value >= max );
}

#ifndef AEMATH_BITSFUNCS

AE_FORCEINLINE AEBOOL32 AE_CALL AEMath::SignBitSet( const AEFLOAT32 &fValue )
{
	return ( *( reinterpret_cast<const unsigned int*>( &fValue ) ) & 0x80000000 ) ? true : false;
}

AE_FORCEINLINE AEBOOL32 AE_CALL AEMath::SignBitSet( const AEDOUBLE64 &dValue )
{
	return ( *( reinterpret_cast<const unsigned long long*>( &dValue ) ) & 0x8000000000000000ull ) ? true : false;
}

#endif // AEMATH_BITSFUNCS

#endif // _AEMATH_INL_
